package com.cloud.charts4a.chart.render;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import com.cloud.charts4a.chart.attrib.AbstractXAxisAttribs;
import com.cloud.charts4a.chart.attrib.YAxisAttribs;
import com.cloud.charts4a.chart.attrib.YRangeHighLightAttribs;
import com.cloud.charts4a.data.AbstractChartData;
import com.cloud.charts4a.data.ChartDataSeries;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;

/**
 * Abstract Chart Render<p/>
 *
 * @param <T1>	subclass of AbstractXAxisAttribs class
 * @param <T2>	subclass of AbstractChartData class
 * 
 * @version
 * <ol>
 * 	<li>2010/07/09 CloudTu：First Release</li>
 * </ol>
 * 
 * @author cloudtu 
 */
abstract class AbstractChartRender<T1 extends AbstractXAxisAttribs<?, ?>,T2 extends AbstractChartData<T2>> {
	/**
	 * 折線點被點擊時，顯示出來提示文字裡，數值部份最多只取到小數第二位
	 */	
	protected static final DecimalFormat tipDecimalFormat = new DecimalFormat("#.##");			
	
	/**
	 * Canvas
	 */
	protected Canvas canvas;
	
	/**
	 * Canvas width
	 */
	protected float canvasWidth;
	
	/**
	 * Canvas Height
	 */
	protected float canvasHeight;
	
	/**
	 * X軸標籤
	 */
	protected String[] xAxisLabels;
	
	/**
	 * Y軸標籤
	 */
	protected int[] yAxisLabels;
	
	/**
	 * 圖例標籤
	 */
	protected String[] legends;
	
	/**
	 * legends's color
	 */
	protected int[] legendColors;
	
	/**
	 * 是否要顯示X軸的單位說明
	 */
	protected boolean isShowXUnitDesc;
	
	/**
	 * 是否要顯示Y軸的單位說明
	 */
	protected boolean isShowYUnitDesc;
	
	/**
	 * 是否要顯示圖例
	 */
	protected boolean isShowLegend;
	
	/**
	 * 是否只顯示折線點
	 */
	protected boolean isOnlyShowDot;
	
	/**
	 * X-Axis attributes in chart
	 */
	protected T1 xAxisAttribs;
	
	/**
	 * Y-Axis attributes in chart
	 */
	protected YAxisAttribs yAxisAttribs;
	
	/**
	 * Y-Axis range highlight attributes
	 */
	protected YRangeHighLightAttribs yRangeHighLightAttribs;
	
	/**
	 * ChartDataSeries list
	 */
	protected List<ChartDataSeries<T2>> chartDataSeriesList = new ArrayList<ChartDataSeries<T2>>();
	
	/**
	 * X軸與Y軸座標的paint object
	 */
	protected Paint xyAxisPaint;
	
	/**
	 * X軸與Y軸的背景格線的paint object
	 */
	protected Paint xyAxisBackgroundGridPaint;		
	
	/**
	 * X軸與Y軸上刻度標籤的 paint object
	 */
	protected Paint xyAxisLabelPaint;	
	
	/**
	 * X軸與Y軸裡單位說明文字的Paint object
	 */
	protected Paint xyAxisUnitDescPaint;	
	
	/**
	 * X-Axis interval pixels of two xAxisLabel unit 
	 */
	protected float xAxisIntervalForTwoXAxisLabelUnit;	
	
	/**
	 * Y-Axis interval pixels of two yAxisLabel unit
	 */
	protected float yAxisIntervalForTwoYAxisLabelUnit;	
	
	/**
	 * X軸裡單位說明文字的字型高度大小為儿Pixel
	 */
	protected float xAxisUnitDescHeight;	
	
	/**
	 * X軸裡單位說明文字的字型高度大小加了上下留白空間後為儿Pixel
	 */
	protected float xAxisUnitDescHeightWithTopAndBottomPad;	
	
	/**
	 * Y軸裡單位說明文字的字型高度大小加了上下留白空間後為儿Pixel
	 */
	protected float yAxisUnitDescHeightWithTopAndBottomPad;
	
	/**
	 * xyAxisLabel height pixels
	 */
	protected float xyAxisLabelHeight;		
	
	/**
	 * xAxisLabel加了上下留白空間後為儿Pixel
	 */
	protected float xAxisLabelHeightWithTopAndBottomPad;	
	
	/**
	 * yAxisLabel加上左右留白空間後為儿Pixel
	 */
	protected float yAxisLabelWidthWithLeftAndRightPad;
	
	/**
	 * one legend height pixels with top and bottom padding
	 */
	protected float oneLegendUnitHeightWithTopAndBottomPad;	
	
	/**
	 * total legends height pixels with top and bottom padding
	 */
	protected float totalLegendsHeightWithTopAndBottomPad;	
	
	protected ChartRenderBo chartRenderBo;	

	/**
	 * Draw chart
	 */
	public void drawChart(){				
		drawXYAxisBackgroundGrid();
		drawContents();
		drawBlankRectAboveMaxYAxisValueAndBelowMinYAxisValue();
		drawXAxisLabels();
		drawYAxisLabels();
		drawXAxisUnitDesc();
		drawYAxisUnitDesc();	
		drawLegends();		
	}
	
	/**
	 * initial xAxisLabels variable
	 */
	abstract protected void initXAxisLabelsValue();	
	
	/**
	 * initial yAxisLabels variable
	 */		
	protected void initYAxisLabelsValue(){						
		//當(yAxisAttribs.getMinYValue() != null && yAxisAttribs.getMaxYValue() != null)時，yAxisLabels裡的最大與最小值直接指定，
		//不需再依chartDataSeriesList裡的內容去動態算出yAxisLabels的最大與最小值
		if(yAxisAttribs.getMinYValue() != null && yAxisAttribs.getMaxYValue() != null){
			yAxisLabels = new int[(yAxisAttribs.getMaxYValue() - yAxisAttribs.getMinYValue())/yAxisAttribs.getYValueInterval() + 1];		
			for(int i=0;i<yAxisLabels.length;i++){
				yAxisLabels[i] = yAxisAttribs.getMinYValue() + yAxisAttribs.getYValueInterval() * i;
			}			
		}
		else{
			//當(chartDataSeriesList.size() == 0 && yRangeHighLightAttribs == null)時，不去做yAxisLabels之值的運算，只設成二個值
			if(chartDataSeriesList.size() == 0 && yRangeHighLightAttribs == null){
				yAxisLabels = new int[2];
				yAxisLabels[1] = yAxisAttribs.getYValueInterval();
				return;
			}			
			
			int minYValue = Integer.MAX_VALUE;
			int maxYValue = Integer.MIN_VALUE;
				
			if(chartDataSeriesList.size() > 0){
				for (ChartDataSeries<T2> chartDataSeries : chartDataSeriesList) {
					if(chartDataSeries.getMinYValueInAllData() < minYValue){
						minYValue = (int)chartDataSeries.getMinYValueInAllData();
					}
					if(chartDataSeries.getMaxYValueInAllData() > maxYValue){
						maxYValue = (int)Math.ceil(chartDataSeries.getMaxYValueInAllData());
					}			
				}				
			}					
			
			if(yRangeHighLightAttribs != null && yRangeHighLightAttribs.getMinYValue() < minYValue){
				minYValue = yRangeHighLightAttribs.getMinYValue();
			}
			if(yRangeHighLightAttribs != null && yRangeHighLightAttribs.getMaxYValue() > maxYValue){
				maxYValue = yRangeHighLightAttribs.getMaxYValue();
			}		

			if(yAxisAttribs.getYValueInterval() >= 5){
				//minYValue之值設成相鄰數值裡可被5整除的最小值
				minYValue = (int)Math.floor((double)minYValue / 5) * 5;									
			}		
			//minYValue與maxYValue之值必需能被yInterval整除。無法整除時，maxYValue自動增量至可被整除之值
			maxYValue = minYValue + (int)Math.abs(Math.ceil(((double)maxYValue - minYValue) / yAxisAttribs.getYValueInterval())) * yAxisAttribs.getYValueInterval();		
			
			yAxisLabels = new int[(maxYValue - minYValue)/yAxisAttribs.getYValueInterval() + 1];		
			for(int i=0;i<yAxisLabels.length;i++){
				yAxisLabels[i] = minYValue + yAxisAttribs.getYValueInterval() * i;
			}			
		}		
	}	
	
	/**
	 * initial legends and legendColors variable
	 */		
	protected void initLegendsValue(){
		if(isShowLegend == true && chartDataSeriesList.size() > 0){
			legends = new String[chartDataSeriesList.size()];
			legendColors = new int[chartDataSeriesList.size()];
			
			for(int i=0;i<chartDataSeriesList.size();i++){
				legends[i] = "■ " + chartDataSeriesList.get(i).getName();
				legendColors[i] = chartDataSeriesList.get(i).getColor();
			}			
		}		
	}	
	
	/**
	 * Draw yAxisLabels
	 */
	private void drawYAxisLabels(){
		float xPosition = yAxisUnitDescHeightWithTopAndBottomPad + yAxisLabelWidthWithLeftAndRightPad / 2;
		float yPosition = xAxisLabelHeightWithTopAndBottomPad + xyAxisLabelHeight / 2;
		int counter = 0;
		for(int i= yAxisLabels.length -1;i>=0;i--){
			canvas.drawText("" + yAxisLabels[i], xPosition, yPosition + counter * yAxisIntervalForTwoYAxisLabelUnit, xyAxisLabelPaint);
			counter++;
		}	
	}
	
	/**
	 * Draw xAxisLabels
	 */
	private void drawXAxisLabels(){
		float xPosition = yAxisUnitDescHeightWithTopAndBottomPad + yAxisLabelWidthWithLeftAndRightPad + (xAxisIntervalForTwoXAxisLabelUnit / 2);
		float yPosition = xAxisLabelHeightWithTopAndBottomPad + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1) + chartRenderBo.textPadding + xyAxisLabelHeight;	
		for(int i= 0;i<xAxisLabels.length;i++){
			canvas.drawText(xAxisLabels[i], xPosition + i * xAxisIntervalForTwoXAxisLabelUnit, yPosition, xyAxisLabelPaint);
		}
	}	
	
	/**
	 * Draw xAxisUnitDesc
	 */
	private void drawXAxisUnitDesc(){
		if(isShowXUnitDesc == true){
			float xPosition = canvasWidth / 2;
			float yPosition = xAxisLabelHeightWithTopAndBottomPad * 2 + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1) + chartRenderBo.textPadding + xAxisUnitDescHeight;
			canvas.drawText(xAxisAttribs.getXUnitDesc(), xPosition, yPosition, xyAxisUnitDescPaint);			
		}
	}		
	
	/**
	 * Draw yAxisUnitDesc
	 */
	private void drawYAxisUnitDesc(){
		if(isShowYUnitDesc == true){
			canvas.save();
			float xPosition = (canvasHeight - totalLegendsHeightWithTopAndBottomPad) / 2;
			float yPosition = - chartRenderBo.textPadding;
			canvas.rotate(90);
			canvas.drawText(yAxisAttribs.getYUnitDesc(), xPosition, yPosition, xyAxisUnitDescPaint);	
			canvas.restore();
		}
	}		
	
	/**
	 * Draw legends
	 */
	private void drawLegends(){
		if(isShowLegend == true && chartDataSeriesList.size() > 0){
			float checkedWidth = 0;		
			float xPosition = 0;
			float yPosition = xAxisLabelHeightWithTopAndBottomPad * 2 + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1) +
							  xAxisUnitDescHeightWithTopAndBottomPad + oneLegendUnitHeightWithTopAndBottomPad - chartRenderBo.textPadding;	
			for(int i=0;i<legends.length;i++){
				float oneLegendUnitWidthWithLeftAndRightPad = chartRenderBo.calSpecifiedLegendWidthWithLeftAndRightPad(legends[i]);
				checkedWidth = checkedWidth + oneLegendUnitWidthWithLeftAndRightPad; 
				if(canvasWidth >= checkedWidth){
					xPosition = xPosition + oneLegendUnitWidthWithLeftAndRightPad / 2;			
				}
				else{
					xPosition = oneLegendUnitWidthWithLeftAndRightPad / 2;
					yPosition = yPosition + oneLegendUnitHeightWithTopAndBottomPad;
					checkedWidth = oneLegendUnitWidthWithLeftAndRightPad;				
				}
						
				Paint legendPaint = chartRenderBo.createLegendPaint(legendColors[i]);
				canvas.drawText(legends[i], xPosition, yPosition, legendPaint);
				xPosition = xPosition + oneLegendUnitWidthWithLeftAndRightPad / 2;			
			}			
		}
	}		
	
	/**
	 * Draw XYAxis background grid
	 */
	private void drawXYAxisBackgroundGrid(){
		float xPosition = yAxisUnitDescHeightWithTopAndBottomPad + yAxisLabelWidthWithLeftAndRightPad;
		float yPosition = xAxisLabelHeightWithTopAndBottomPad;

		//畫X軸
		canvas.drawLine(xPosition, yPosition + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit,
						xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * (xAxisLabels.length - 1),
						yPosition + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit , xyAxisPaint);
		//畫Y軸
		canvas.drawLine(xPosition, yPosition , xPosition, yPosition + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1), xyAxisPaint);
							
		//畫X軸格線		
		for(int i=0;i<yAxisLabels.length-1;i++){
			canvas.drawLine(xPosition, yPosition + i * yAxisIntervalForTwoYAxisLabelUnit,
							xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * (xAxisLabels.length - 1),
							yPosition + i * yAxisIntervalForTwoYAxisLabelUnit, xyAxisBackgroundGridPaint);
		}		
		//畫Y軸格線
		for(int j=0;j<xAxisLabels.length;j++){
			canvas.drawLine(xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * j, yPosition ,
							xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * j,
							yPosition + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1), xyAxisBackgroundGridPaint);
		}			
	}		
	
	/**
	 * 畫二個黑色區塊把超過Y軸最大值與小於Y軸最小值的折線給蓋住
	 */	
	private void drawBlankRectAboveMaxYAxisValueAndBelowMinYAxisValue(){	
		Paint blackPaint = new Paint();
		blackPaint.setColor(Color.BLACK);		
		RectF aboveRect = new RectF(0,0,canvasWidth,xAxisLabelHeightWithTopAndBottomPad);		
		canvas.drawRect(aboveRect, blackPaint);	//畫黑色區塊把超過Y軸最大值的折線給蓋住
		
		RectF belowRect = new RectF(0,xAxisLabelHeightWithTopAndBottomPad + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit,canvasWidth,canvasHeight);		
		canvas.drawRect(belowRect, blackPaint);	//畫黑色區塊把小於Y軸最小值的折線給蓋住		
		
		float xPosition = yAxisUnitDescHeightWithTopAndBottomPad + yAxisLabelWidthWithLeftAndRightPad;
		float yPosition = xAxisLabelHeightWithTopAndBottomPad;
		//畫最上方的X軸格線
		canvas.drawLine(xPosition, yPosition,
						xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * (xAxisLabels.length - 1),
						yPosition, xyAxisBackgroundGridPaint);				
		//畫最下方的X軸
		canvas.drawLine(xPosition, yPosition + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit,
						xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * (xAxisLabels.length - 1),
						yPosition + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit , xyAxisPaint);			
	}	
	
	/**
	 * Draw all contents in XY-Axis
	 */
	abstract protected void drawContents();
	
	/**
	 * 當折線點被點擊時，顯示相關的提示文字
	 * 
	 * @param chartData	ChartData
	 */
	public void drawTipOnChartWhenDotIsClicked(T2 chartData){
		//當量測值比Y軸上的最大值還大，或是比Y軸上的最小值還小時，不顯示折線點的提示文字
		if(chartData.getYValue() < yAxisLabels[0] || chartData.getYValue() > yAxisLabels[yAxisLabels.length - 1]){	
			return;
		}		
		
		Rect tipRect = chartData.getTipRect();
		Path tipBackGroundPath = new Path();
		Paint tipBackGroundPaint = chartRenderBo.createTipBackgroundRectPaint();
		Paint tipTextPaint = chartRenderBo.createTipTextPaint();
		
		if(tipRect.top > chartData.getYPosition()){
			tipBackGroundPath.moveTo(chartData.getXPosition(), chartData.getYPosition() + 2 * chartRenderBo.dataSeriesDotRadias);
			tipBackGroundPath.lineTo(chartData.getXPosition() - 2 * chartRenderBo.dataSeriesDotRadias, ((float)tipRect.top + tipRect.bottom)/2);
			tipBackGroundPath.lineTo(chartData.getXPosition() + 2 * chartRenderBo.dataSeriesDotRadias, ((float)tipRect.top + tipRect.bottom)/2);
			tipBackGroundPath.close();
			tipBackGroundPath.addRoundRect(new RectF(tipRect.left,((float)tipRect.top + tipRect.bottom)/2,tipRect.right,tipRect.bottom),
								 chartRenderBo.tipRectCornerRadius, chartRenderBo.tipRectCornerRadius, Path.Direction.CW);			
			canvas.drawPath(tipBackGroundPath, tipBackGroundPaint);						
			canvas.drawText(chartData.getTipText(), ((float)tipRect.left + tipRect.right)/2, tipRect.bottom - chartRenderBo.tipTextPadding, tipTextPaint);				
		}
		else{
			tipBackGroundPath.addRoundRect(new RectF(tipRect.left,tipRect.top,tipRect.right,((float)tipRect.top + tipRect.bottom)/2),
					 			 chartRenderBo.tipRectCornerRadius, chartRenderBo.tipRectCornerRadius, Path.Direction.CW);	
			tipBackGroundPath.moveTo(chartData.getXPosition(), chartData.getYPosition() - 2 * chartRenderBo.dataSeriesDotRadias);
			tipBackGroundPath.lineTo(chartData.getXPosition() - 2 * chartRenderBo.dataSeriesDotRadias, ((float)tipRect.top + tipRect.bottom)/2);
			tipBackGroundPath.lineTo(chartData.getXPosition() + 2 * chartRenderBo.dataSeriesDotRadias, ((float)tipRect.top + tipRect.bottom)/2);
			tipBackGroundPath.close();
			canvas.drawPath(tipBackGroundPath, tipBackGroundPaint);	
			canvas.drawText(chartData.getTipText(), ((float)tipRect.left + tipRect.right)/2,
													 ((float)tipRect.top + tipRect.bottom)/2 - chartRenderBo.tipTextPadding, tipTextPaint);				
		}				
	}			
}
